using CSharpier.Core;
using CSharpier.Core.CSharp;
using FluentAssertions;
using Microsoft.CodeAnalysis.CSharp;
using NUnit.Framework;

namespace CSharpier.Tests.CSharp;

[TestFixture]
[Parallelizable(ParallelScope.All)]
internal sealed class CSharpFormatterTests
{
    [Test]
    public void Format_Should_Use_Default_Width()
    {
        var code = "var someVariable = someValue;";
        var result = CSharpFormatter.Format(code);

        result.Code.Should().Be("var someVariable = someValue;\n");
        result.CompilationErrors.Should().BeEmpty();
    }

    [Test]
    public void Format_Should_Return_Compilation_Errors()
    {
        var code = "var someVariable = someValue";
        var result = CSharpFormatter.Format(code);

        result.Code.Should().Be(code);
        result.CompilationErrors.Should().ContainSingle();
    }

    [Test]
    public void Format_Should_Use_Width()
    {
        var code = "var someVariable = someValue;";
        var result = CSharpFormatter.Format(code, new CodeFormatterOptions { Width = 10 });

        result.Code.Should().Be("var someVariable =\n    someValue;\n");
    }

    [Test]
    public void Format_Should_Measure_Regular_Characters()
    {
        var code = """
            var x = "123456";
            """;
        var result = CSharpFormatter.Format(code, new CodeFormatterOptions { Width = 20 });

        result.Code.Should().Be("var x = \"123456\";\n");
    }

    [Test]
    public void Format_Should_Measure_Wide_Characters()
    {
        var code = """
            var x = "가가가가가가";
            """;
        var result = CSharpFormatter.Format(code, new CodeFormatterOptions { Width = 20 });

        result.Code.Should().Be("var x =\n    \"가가가가가가\";\n");
    }

    [Test]
    public void Format_Should_Use_IndentStyle()
    {
        var code = "void someMethod() { var someVariable = someValue; }";
        var result = CSharpFormatter.Format(
            code,
            new CodeFormatterOptions { IndentStyle = IndentStyle.Tabs, IndentSize = 1 }
        );

        result.Code.Should().Be("void someMethod()\n{\n\tvar someVariable = someValue;\n}\n");
    }

    [Test]
    public void Format_Should_Use_IndentWidth()
    {
        var code = "void someMethod() { var someVariable = someValue; }";
        var result = CSharpFormatter.Format(
            code,
            new CodeFormatterOptions { IndentStyle = IndentStyle.Spaces, IndentSize = 1 }
        );

        result.Code.Should().Be("void someMethod()\n{\n var someVariable = someValue;\n}\n");
    }

    [Test]
    public void Format_Should_Use_EndOfLine()
    {
        var code = "var someVariable = someValue;";
        var result = CSharpFormatter.Format(
            code,
            new CodeFormatterOptions { EndOfLine = EndOfLine.CRLF }
        );

        result.Code.Should().Be("var someVariable = someValue;\r\n");
    }

    [Test]
    public void Format_Should_Ignore_Generated_Files()
    {
        var code = """
// <auto-generated>
var someVariable =   someValue;
""";
        var result = CSharpFormatter.Format(code, new CodeFormatterOptions());

        result.Code.Should().Be(code);
    }

    [Test]
    public void Format_Should_Format_Generated_Files()
    {
        var code = """
// <auto-generated>
var someVariable =   someValue;

""";
        var result = CSharpFormatter.Format(
            code,
            new CodeFormatterOptions { IncludeGenerated = true }
        );

        result.Code.Should().Be(code.Replace(" =   ", " = "));
    }

    [TestCase("\n")]
    [TestCase("\r\n")]
    public void Format_Should_Get_Line_Endings_With_SyntaxTree(string lineEnding)
    {
        var code = $"public class ClassName {{{lineEnding}}}";
        var syntaxTree = CSharpSyntaxTree.ParseText(code);
        var result = CSharpFormatter.Format(syntaxTree);

        result.Code.Should().Be("public class ClassName { }" + lineEnding);
    }
}
